/*
 *  levelUpGame - GameEventHandler.cpp
 *  Copyright 2011 W. Reckman. All rights reserved.
 */
#include <string>
#include "Settings.h"
#include "GameModel.h"
#include "LevelUpApp.h"
#include "GameEventHandler.h"

using std::string;

//--------------------------------------------------------------
GameEventHandler::GameEventHandler( LevelUpApp& appRef_ )
: appRef( appRef_ ), game( appRef_.getGame() ), s( Settings::getInstance() )
{}

//--------------------------------------------------------------
void GameEventHandler::setup() {
	turnMinPitch = ofToFloat( *s.getValueInGroup( "snd_turn_min_pitch", "game", "0.9" ) );
	turnMaxPitch = ofToFloat( *s.getValueInGroup( "snd_turn_max_pitch", "game", "1.1" ) );
	maxNumPickups = ofToInt( *s.getValueInGroup( "max_num_pickups", "game", "15" ) );
	playerNoHitTime = ofToFloat( *s.getValueInGroup( "player_eaten_nohit_extra_time", "game", "0.5" ) );
	gamePrecountTime = ofToFloat( *s.getValueInGroup( "game_precount_time", "game", "3" ) );
	gameStartNoHitTime = ofToFloat( *s.getValueInGroup( "game_start_nohit_time", "game", "0.5" ) );
	endSequenceAnimTime = ofToFloat( *s.getValueInGroup( "game_end_anim_time", "game", "0.5" ) );
	endSequenceDisplayTime = ofToFloat( *s.getValueInGroup( "game_end_display_time", "game", "0.5" ) );
	resetPlayerOnGhostHit = parseBool( *s.getValueInGroup( "player_eaten_reset_position", "game", "true" ) );
	startTextShowTime = ofToInt( *s.getValueInGroup( "game_start_text_time", "game", "2.5f" ) );
}


//--------------------------------------------------------------
void GameEventHandler::onGameEvent( EventArgsGame& ev_ ) {
	//if ( ev_.eventType != EventArgsGame::GE_NEW_FRAME ) logGameEvent( ev_ );
	
	switch ( ev_.eventType ) {
		case EventArgsGame::GE_NEW_FRAME:
			break;
			
		case EventArgsGame::GE_PLAYER:
			handlePlayerEvent( ev_ );
			break;
			
		case EventArgsGame::GE_GAME:
			handleGameEvent( ev_ );
			break;
			
		case EventArgsGame::GE_PLAYER_QUEUED:
ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) QUEUEING PLAYER (p=%i, q=%i)", game.getPlayers().size(), appRef.getGameController().getQueuedPlayerCount() ); //TEMP
			if ( game.getPlayers().size() < game.getMaxNumPlayers() ) {
				appRef.getGameController().createPlayerFromQueue( ev_.intVal );
			}
ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) PLAYER QUEUED (p=%i, q=%i)", game.getPlayers().size(), appRef.getGameController().getQueuedPlayerCount() ); //TEMP
//			if ( ! game.isGamePlaying() && game.getPlayers().size() == game.getMaxNumPlayers() ) {
//ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) triggering new game because enough players have been created from queue" ); //TEMP
//				appRef.getGameController().changeGameState( GS_PRECOUNT );
//			}
			if ( game.getState() == GS_IDLE && game.getPlayers().size() == game.getMaxNumPlayers() ) {
ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) triggering new game because enough players have been created from queue" ); //TEMP
				appRef.getGameController().changeGameState( GS_PRECOUNT );
			}
			break;
			
		case EventArgsGame::GE_OCREATED:
			if ( ev_.objType == EventArgsGame::GOT_PLAYER ) {
				Player* p = ev_.obj.player;
				p->setBlocked( false );
				p->startNonCorporealTimeout( 5 ); //TEMP
			}
			break;
			
		case EventArgsGame::GE_ODESTROYED:
			if ( ev_.objType == EventArgsGame::GOT_PICKUP ) {
				if ( game.getPickups().size() < maxNumPickups ) {
					//NOTE: force balance between culture and industry pickups
					int cpc = 0, ipc = 0;
					for ( int i = 0; i < game.getPickups().size(); ++i ) {
						if ( game.getPickups()[i]->getBalanceType() == BK_CULTURE ) cpc++;
						else if ( game.getPickups()[i]->getBalanceType() == BK_INDUSTRY ) ipc++;
					};
					bool genCulturePickup = ( cpc < ipc ) ? true : ( ( cpc > ipc ) ? false : roundf( ofRandom( 0, 1 ) ) );
					game.spawnPickup( genCulturePickup ? BK_CULTURE : BK_INDUSTRY );
				}
			}
			break;
	}
}

/*********************
 * PRIVATE FUNCTIONS *
 *********************/

//--------------------------------------------------------------
void GameEventHandler::handleGameEvent( EventArgsGame& ev_ ) {
	switch ( ev_.eventSubtype ) {
		case EventArgsGame::GES_GAME_INIT: {
			GameController& gCtl = appRef.getGameController();
			if ( gCtl.getQueuedPlayerCount() < game.getMaxNumPlayers() ) {
				ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) GAME_INIT (images have been requested from photobooth)" ); //TEMP
#ifdef REQUEST_NEW_PLAYERS_ON_INIT
				gCtl.requestNewPlayersFromPhotobooth();
#endif
			} else {
				ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) GAME_INIT (not requesting images from photobooth, still got %i in stock)", gCtl.getQueuedPlayerCount() ); //TEMP
			}
			break;
		}
		case EventArgsGame::GES_GAME_BALANCE_CHANGED:
			appRef.getOscDataSender().sendGameBalance( ev_.floatVal );
			break;
		case EventArgsGame::GES_GAME_PRECOUNT_TICK: {
			AudioController& aCtl = appRef.getAudioController();
			switch ( ev_.intVal ) {
				case 9:
#ifdef RANDOMIZE_10SEC_PRECOUNT_SOUND
					aCtl.play( ( roundf( ofRandom( 0, 1 ) ) ) ? AudioController::SND_VOICE_GETREADY : AudioController::SND_VOICE_POSITIONS );
#else
					aCtl.play( AudioController::SND_VOICE_GETREADY );
#endif
					break;
				case 2: aCtl.play( AudioController::SND_VOICE_3 ); break;
				case 1: aCtl.play( AudioController::SND_VOICE_2 ); break;
				case 0: aCtl.play( AudioController::SND_VOICE_1 ); break;
			}
			break;
		}
		case EventArgsGame::GES_GAME_STATE:
			appRef.getOscDataSender().sendGameState( ev_.prevState, ev_.currState );
			handleGameStateChange( ev_ );
			break;
	}
}

//--------------------------------------------------------------
void GameEventHandler::handleGameStateChange( EventArgsGame& ev_ ) {
	GameController& gCtl = appRef.getGameController();
	AudioController& aCtl = appRef.getAudioController();
	GameModel::vec_PlayerP& players = game.getPlayers();
ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) GAME_STATE (%s -> %s)", gameStateText( ev_.prevState ).c_str(), gameStateText( ev_.currState ).c_str() ); //TEMP
	
	switch ( ev_.currState ) {
		case GS_IDLE:
			break;
			
		case GS_PRECOUNT:
ofLog( OF_LOG_NOTICE, "(GameEventHandler.cpp) generating %i pickups", maxNumPickups - game.getPickups().size() ); //TEMP
			for ( int i = game.getPickups().size(); i < maxNumPickups; ++i ) {
				bool genCulturePickup = roundf( ofRandom( 0, 1 ) );
				game.spawnPickup( genCulturePickup ? BK_CULTURE : BK_INDUSTRY );
			}
			
			for ( int i = 0; i < players.size(); ++i ) {
				players[i]->setBlocked( true );
				players[i]->setCollidable( false );
			}
			
			game.resetGhostPositions();
			
			aCtl.setLoop( AudioController::SND_COUNTDOWN_START, true );
			aCtl.play( AudioController::SND_COUNTDOWN_START );
			game.runPrecount( gamePrecountTime );
			break;
			
		case GS_PLAYING:
#ifdef REQUEST_PLAYERS_ON_GAME_START
			if ( gCtl.getQueuedPlayerCount() < game.getMaxNumPlayers() ) {
				appRef.getGameController().requestNewPlayersFromPhotobooth();
				ofLog( OF_LOG_NOTICE, "(GameEventHandler.cpp) requested new photobooth images" );
			} else {
				ofLog( OF_LOG_NOTICE, "(GameEventHandler.cpp) not requesting new photobooth images, still got %i in stock", gCtl.getQueuedPlayerCount() );
			}
#endif
			
			aCtl.stop( AudioController::SND_COUNTDOWN_START );
			aCtl.play( AudioController::SND_COUNTDOWN_END );
			aCtl.play( AudioController::SND_VOICE_GO );
			game.showTimedText( "!!! GO !!!", startTextShowTime );
			
			for ( int i = 0; i < players.size(); ++i ) {
				players[i]->setBlocked( false );
				players[i]->startNonCorporealTimeout( gameStartNoHitTime );
			}
			
			game.resetBalance();
			game.resetPlayTime();
			break;
			
		case GS_END_SEQUENCE: /* NOTE: intVal means sequence stage!! */
			if ( ev_.intVal == 1 ) {
#ifdef REQUEST_PLAYERS_ON_GAME_END_SEQUENCE
				if ( gCtl.getQueuedPlayerCount() < game.getMaxNumPlayers() ) {
					appRef.getGameController().requestNewPlayersFromPhotobooth();
					ofLog( OF_LOG_NOTICE, "(GameEventHandler.cpp) requested new photobooth images" );
				} else {
					ofLog( OF_LOG_NOTICE, "(GameEventHandler.cpp) not requesting new photobooth images, still got %i in stock", gCtl.getQueuedPlayerCount() );
				}
#endif
				
				//animated scale-up of winning player
				game.determineWinner();
				aCtl.setPan( AudioController::SND_WIN, game.getPanPosition( players[game.getWinningPlayer()]->getPosition().x ) );
				aCtl.play( AudioController::SND_WIN );
				gCtl.setEndSequenceStage( 1 );
				gCtl.setEndSequenceTimer( endSequenceAnimTime );
				
				/* send player data to scoreboard */
				const OscDataSender& dataSender = appRef.getOscDataSender();
				for ( int i = 0; i < players.size(); ++i ) {
					dataSender.sendPlayerData( players[i]->getInfo() );
				}				
			} else if ( ev_.intVal == 2 ) {
				//keep displaying the winning player until this time is up
				gCtl.setEndSequenceStage( 2 );
				gCtl.setEndSequenceTimer( endSequenceDisplayTime );
			}
			break;
			
		case GS_END:
			GameController& gCtl = appRef.getGameController();
			
			//NOTE: cannot use for loop because items are being deleted inside the loop
			while ( players.size() > 0 ) {
				game.destroyPlayer( players[0] );
			}
			
			while ( gCtl.getQueuedPlayerCount() > 0 &&
						 players.size() < game.getMaxNumPlayers() ) {
				gCtl.createPlayerFromQueue( -1 );
			}
			
			if ( players.size() < game.getMaxNumPlayers() ) {
				ofLog( OF_LOG_WARNING, "(GameEventHandler.cpp) could not create new players (missing %i), idling game until more players are available",
							game.getMaxNumPlayers() - players.size() );
				gCtl.changeGameState( GS_IDLE );
			} else {
				gCtl.changeGameState( GS_PRECOUNT );
			}
			break;
	}
}

//--------------------------------------------------------------
void GameEventHandler::handlePlayerEvent( EventArgsGame& ev_ ) {
	AudioController& aCtl = appRef.getAudioController();
	Player* p = ev_.obj.player;
	
	switch ( ev_.eventSubtype ) {
		case EventArgsGame::GES_PLAYER_TURN:
			aCtl.setSpeed( AudioController::SND_PLAYER_TURN, ofRandom( turnMinPitch, turnMaxPitch ), p->getId() );
			aCtl.setPan( AudioController::SND_PLAYER_TURN, game.getPanPosition( p->getPosition().x ), p->getId() );
			aCtl.play( AudioController::SND_PLAYER_TURN, p->getId() );
			break;
			
		case EventArgsGame::GES_PLAYER_UNFREEZE:
			p->setBlocked( false );
			if ( resetPlayerOnGhostHit ) { p->resetPosition(); p->resetPosition(); } //NOTE: set twice to prevent this position from being reversed
			p->startNonCorporealTimeout( playerNoHitTime );
			
			aCtl.stop( AudioController::SND_IDLE );
			aCtl.setPan( AudioController::SND_PLAYER_ID, game.getPanPosition( p->getPosition().x ), p->getId() );
			aCtl.play( AudioController::SND_PLAYER_ID, p->getId() );
			break;
			
		case EventArgsGame::GES_PLAYER_CORPOREALIZE:
			p->setCollidable( true );
			break;
		case EventArgsGame::GES_PLAYER_PRECOUNT_MOVE_ATTEMPT:
#ifdef ENABLE_SOUND_ON_PRECOUNT_MOVE
			aCtl.setPan( AudioController::SND_PLAYER_ID, game.getPanPosition( p->getPosition().x ), p->getId() );
			aCtl.play( AudioController::SND_PLAYER_ID, p->getId() );
#endif
			break;
	}
}


//--------------------------------------------------------------
void GameEventHandler::logGameEvent( EventArgsGame& ev_ ) {
	if ( ev_.eventType == EventArgsGame::GE_GAME ) { ofLog( OF_LOG_NOTICE, "received game event GE_GAME" ); }
	else if ( ev_.eventType == EventArgsGame::GE_PLAYER_QUEUED ) { ofLog( OF_LOG_NOTICE, "received game event GE_PLAYER_QUEUED" ); }
	else if ( ev_.eventType == EventArgsGame::GE_NEW_FRAME ) { ofLog( OF_LOG_NOTICE, "received game event GE_NEW_FRAME" ); }
	else if ( ev_.eventType == EventArgsGame::GE_PLAYER ) { ofLog( OF_LOG_NOTICE, "received game event GE_PLAYER" ); }
	else {
		if ( ev_.objType == EventArgsGame::GOT_PICKUP ) {
			ofLog( OF_LOG_NOTICE, "received game event %s with pickup",
						( ev_.eventType == EventArgsGame::GE_OCREATED ) ? "GE_OCREATED" : "GE_ODESTROYED" );
		} else if ( ev_.objType == EventArgsGame::GOT_PLAYER ) {
			ofLog( OF_LOG_NOTICE, "received game event %s with player %i",
						( ev_.eventType == EventArgsGame::GE_OCREATED ) ? "GE_OCREATED" : "GE_ODESTROYED",
						ev_.obj.player->getId() );
		}
	}
}
